home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
001
/
xmodem.arc
/
XMDM2.C
< prev
next >
Wrap
Text File
|
1985-06-15
|
14KB
|
496 lines
/*---------------------------------------------------------------
XMODEM.C
By Donald G. Krantz 4/84
(C) Copyright 1984 Donald G. Krantz
All Rights Reserved
Christensen Protocol Routines for XMODEM.C
This file contains a stand-alone Christensen Engine, which
requires the following interface to another system:
error() is called to terminate a transfer: the parameter is a
string describing the error condition.
VOID error( str )
char *str;
abort() is called at strategic places to determine if the
local operator wants to quit sending/receiving.
VOID abort()
rx() picks up a character from the remote.
char rx()
rxstat() returns TRUE when a character is ready from the
remote.
int rxstat()
tx() sends a character to the remote.
VOID tx( ch )
char ch;
lcl_str() sends an informational message to the local
console.
lcl_str( str )
char *str;
---------------------------------------------------------------*/
#include "stdio.h" /* ECO C */
#include "b:xmodem.h"
/*---------------------------------------------------------------
txfile() does transmission of a file.
Calling sequence: "fd" is a file pointer to the open file
to be transmitted.
References globals: buffer;
Modifies globals: rec, crc;
---------------------------------------------------------------*/
VOID txfile( fd )
FILE *fd;
{
char ch; /* scratch character */
char *i; /* buffer pointer (index) */
int y; /* scratch counter */
rec = 1; /* uses "natural" numbering */
while( rxstat() )
rx();
while( TRUE ) /* handshake w/ receiver */
{
if( (ch = wait( 10, TRUE, TRUE )) == NAK )
{
crc = FALSE;
break;
}
if( ch == CRC )
{
crc = TRUE;
break;
}
abort();
}
while( fillbuf( fd, buffer ) ) /* file not empty? */
{
txrec( buffer ); /* send record */
abort();
}
tx( EOT );
while( (ch = wait( 1, TRUE, TRUE )) != ACK )
{
tx( EOT ); /* show end */
}
fclose( fd ); /* dump file */
}
/*---------------------------------------------------------------
txname() transmits a file name from the CP/M fcb format
parameter 'name'.
References globals: buffer, checksum, crc;
Modifies globals: crc;
---------------------------------------------------------------*/
VOID txname( name )
char *name;
{
register int i; /* scratch counter */
char ch; /* scratch char */
char crcsav; /* holds state of global 'crc' */
crcsav = crc; /* we always use checksum for */
crc = FALSE; /* name error detection */
while( TRUE ) /* main loop for retries */
{
while( rxstat() )
rx();
i = 0; /* retry count */
while( TRUE )
{
if( wait( RETRY, TRUE, TRUE ) == NAK )
break;
if( i++ > RETRY )
error( "Can't send filename" );
}
tx( ACK ); /* handshake name request */
sleep(); /* decent interval */
clrcrc(); /* clear checksum accumulator */
for( i = 0 ; i < NAMESIZE ; i++ )/* name loop */
{
tx( name[ i ] );
wait( 10, TRUE, TRUE );/* wait for ACK */
}
tx( EoF ); /* name terminated w/EoF*/
if(wait(10,FALSE,TRUE)==checksum) /* handshake */
{
tx( ACK ); /* handshake OK checksum*/
crc = crcsav; /* replacce 'crc' */
return; /* normal exit */
}
tx( BADNAME ); /* handshake bad chksm */
}
}
/*---------------------------------------------------------------
txrec() transmits a single record, with retransmit on error from
receiver. Input is a pointer to the I/O buffer.
References globals: rec, crcaccum, checsum, crc;
Modifies globals: rec;
---------------------------------------------------------------*/
VOID txrec( buf )
char *buf;
{
register int i;
unsigned cr;
while( TRUE ) /* do it until right */
{
sprintf( msg, "\rTransmitting record %d ", rec );
lcl_str( msg );
tx( SOH ); /* start of header */
tx( rec ); /* rec # */
tx( ~rec ); /* 1's comp */
clrcrc(); /* clear CRC accum */
for( i = 0 ; i < RECSIZE ; i++ )
tx( buf[ i ] ); /* send record */
updcrc( 0 ); /* finish up CRC */
updcrc( 0 ); /* again */
cr = crcaccum; /* save crc lobyte */
if( crc ) /* send hi byte first */
{
tx( crcaccum >> 8 );
tx( cr );
}
else
tx( checksum );
if( wait(10, TRUE, TRUE)==ACK)/* quit if correct*/
break;
}
rec++; /* bump record count */
}
/*---------------------------------------------------------------
rxname() loads a CP/M style fcb with a filename from remote
sender. On receipt of an EOT instead of a name character, exits
---------------------------------------------------------------*/
VOID rxname( fcb )
char *fcb; /* points to CP/M style fcb */
{
char *fcbptr; /* index to fcb */
register int i; /* scratch counter */
int ch; /* scratch char (must hold ERR) */
char chksum; /* checksum accumulator */
while( TRUE )
{
fcbptr = fcb; /* align index */
i = RETRY * 5; /* retry for name h.s. */
while( TRUE ) /* Handshake NAK */
{
abort(); /* check operator abort */
tx( NAK );
if( wait( 1, FALSE, FALSE) == ACK )
break;
if( !( i-- ) )
error( "Timed out waiting for name" );
}
chksum = EoF; /* init checksum */
for( i = 0 ; i < 34 ; i++ ) /* accept noise */
{
if( (ch = wait(1,FALSE,FALSE)) == EOT )
{
tx( ACK );
exit( 0 );
}
if( ch == EoF ) /* End of name chars */
break;
if( ch != ERROR ) /* not timeout */
{
*(fcbptr++) = ch & 0x7F;
chksum += ch & 0x7F;
}
abort(); /* operator abort */
tx( ACK ); /* handshake name char */
}
fcb[ NAMESIZE ] = 0; /* terminate name field */
do {
abort(); /* operator abort */
tx( chksum ); /* handshake checksum */
} while( (ch = wait(1,FALSE,FALSE)) == ERROR );
if( ch == ACK ) /* ACK is good name */
return;
}
}
/*---------------------------------------------------------------
rxfile() receives a file. Input parameter is a file pointer
to the local file receiving the transmitted file.
References globals: buffer, rec, crcaccum, checksum, crc;
Modifies globals: buffer, rec;
---------------------------------------------------------------*/
VOID rxfile( fd )
FILE *fd;
{
char ch; /* scratch handshake var */
char response; /* ACK/NAK/CRC handshake */
char crcrlo; /* rec'd CRC low byte */
char crcrhi; /* rec'd CRC hi byte */
char r1; /* current record number */
char r2; /* 1's comp record number */
unsigned int j; /* wait loop timer */
int i; /* scratch counter */
register char *bptr; /* buffer index */
while( rxstat() )
rx();
rec = 1; /* uses natural numbering */
if( crc ) /* set initial handshake to */
response = CRC; /* CRC or checksum */
else
response = NAK;
while( TRUE ) /* record receive loop */
{
bptr = buffer; /* align index */
sprintf( msg, "\rWaiting for record %d ", rec );
lcl_str( msg );
for( i=1 ; i <= RETRY * 5 ; i++ )
{
abort();
tx( response ); /* send handshake */
if( (ch = wait(1,TRUE,TRUE)) == SOH )
break; /* SOH indicatees rec */
if( ch == EOT ) /* EOT indicates done */
{
fclose( fd );
tx( ACK ); /* handshake */
return; /* normal exit */
}
if( ch == CAN ) /* Xmit request abort */
{
fclose( fd );
error("\rReceived cancel request");
}
if( i == RETRY * 5 ) /* timeout exit */
error( "Can't sync to sender" );
}
r1 = wait(1,FALSE,FALSE); /* record number */
r2 = wait(1,FALSE,FALSE); /* 1's comp record # */
while( bptr - buffer < RECSIZE ) /* test count */
{
*(bptr++)=wait(1,FALSE,FALSE);/*accept char*/
}
if( crc ) /* get hibyte CRC */
crcrhi = wait(1,FALSE,FALSE);
crcrlo = wait(1,FALSE,FALSE);/* lobyte CRC or chksm*/
response = NAK; /* init response */
if( (~r1 & 0xFF) != (r2 & 0xFF) )
continue;
clrcrc(); /* calc checksum/CRC */
for( j = 0 ; j < RECSIZE ; j++ )
updcrc( buffer[ j ] );
updcrc( 0 ); /* required to finish */
updcrc( 0 ); /* off CRC - why? */
if( crc ) /* CRC test */
if( (crcrlo + (crcrhi << 8)) != crcaccum )
continue;
if( !crc ) /* checksum test */
if( crcrlo != checksum )
continue;
if( (r1 == (rec - 1) & 0xFF ) ) /* duplicate? */
{
response = ACK; /* dup is OK - ACK was */
continue; /* trashed - ignore it */
}
if( r1 != (rec & 0xFF) )/* fatal sequence error */
error( "File record numbering error" );
rec++; /* bump record count */
for( j = 0 ; j < RECSIZE ; j++ ) /* write data */
putc( buffer[ j ], fd );
response = ACK; /* normal loop end */
}
}
/*---------------------------------------------------------------
parse() expands non - ambiguous filespecs to the
standard CP/M fcb format, excluding drive byte.
Inputs are "normal" filespec (inspec), and exanded fcb.
---------------------------------------------------------------*/
char *parse( inspec, fcb )
char *inspec, *fcb;
{
register int i; /* fcb index */
int inptr; /* input spec index */
for( i = 0 ; i < NAMESIZE ; i++ )/* blank fill name */
fcb[ i ] = ' ';
if( inspec[ 1 ] == ':' ) /* check for drivspec */
inptr = 2; /* point past drivespec */
else
inptr = 0; /* index to start */
i = 0; /* pointer into fcb */
while( TRUE )
switch( inspec[ inptr++ ] )
{
case '\0': /* end of input spec */
fcb[ NAMESIZE ] = 0;
return( fcb );
case '.': /* extension spec'ed */
i = NAMESIZE - 3; /* extension */
break;
default:
if( i < NAMESIZE )
fcb[ i++ ] = toupper(
inspec[ inptr - 1 ] );
}
}
/*---------------------------------------------------------------
unparse() reassembles a filename from a CP/M fcb into "normal" or
cmpressed form, so that our C functions can deal with them.
Inputs are a pointer to a string to receive the name (name), and
a pointer to a CP/M style fcb entry (buf).
---------------------------------------------------------------*/
char *unparse( name, buf )
char *name, *buf;
{
register int i; /* 'name' index */
int j; /* 'buf' index */
i = 0; /* 'driveless' name */
for( j = 0 ; j < NAMESIZE ; j++ )/* transfer chars */
{
if( buf[ j ] != ' ' ) /* (skip spaces) */
name[ i++ ] = buf[ j ] & 0x7F;
if( j == NAMESIZE-4 ) /* don't forget dot */
name[ i++ ] = '.';
}
name[ i ] = '\0'; /* terminate string */
/* eat terminal dot */
if( *(index( name, '.' ) + 1) == '\0' )
*(index( name, '.' )) = '\0';
return( name ); /* return pointer */
}
/*---------------------------------------------------------------
fillbuf() loads the I/O buffer with a record from the input file.
Returns TRUE if data was available, FALSE if no data left.
---------------------------------------------------------------*/
int fillbuf( fd, buffer )
FILE *fd;
char *buffer;
{
register int i; /* scratch counter */
int errorchk; /* holds EOF in Eco C */
for( i = 0 ; i < RECSIZE ; i++ )
{
if( (errorchk = getc( fd)) == EOF )
break;
buffer[ i ] = errorchk;
}
if( i == 0 ) /* no data read */
return( FALSE );
for( ; i < RECSIZE ; i++ )
buffer[ i ] = 0; /* zero fill at EOF */
return( TRUE );
}
/*---------------------------------------------------------------
clrcrc() clears the crc accumulator. Not much to it, actually.
References globals:
Modifies globals: crcaccum, checksum;
---------------------------------------------------------------*/
VOID clrcrc()
{
crcaccum =
checksum = 0;
}
/*---------------------------------------------------------------
updcrc() updates the crc accumulator, if 'crc' is TRUE, else
updates the checksum.
'x' is the byte to be added to CRC or checksum.
CCITT polynomial.
References globals: crc;
Modifies globals: crcaccum, checksum;
---------------------------------------------------------------*/
VOID updcrc( x )
char x;
{
unsigned shifter, i, flag;
if( crc )
{
for( shifter = 0x80 ; shifter ; shifter >>= 1 )
{
flag = (crcaccum & 0x8000);
crcaccum <<= 1;
crcaccum |= ((shifter & x) ? 1 : 0);
if( flag )
crcaccum ^= 0x1021;
}
}
else
checksum += x;
}
/*---------------------------------------------------------------
sleep() does a short delay to account for transmission line
latency, etc.
---------------------------------------------------------------*/
VOID sleep()
{
register unsigned int i;
for( i=0 ; i < MAGIC_NUMBER ; i++ )
;
}
/*---------------------------------------------------------------
wait() waits for a character - timeout built in. Timeout
condition causes error return to menu.
'time' controls duration of wait.
'is_cmd' TRUE indicates that CAN recvd will cause program exit
'error_out' TRUE indicates timeout causes program exit
---------------------------------------------------------------*/
int wait( time, is_cmd, error_out )
int time;
{
register unsigned int i; /* loop timer */
int j; /* timeout count */
int ch;
j = /* timeout count */
i = 0; /* inner timeout count */
while( !rxstat() )
if( i++ > MAGIC_NUMBER )/* about 1.5 secs */
if( !(time--) ) /* check retry count */
if( error_out )
error( "Receiver timed out" );
else
return( ERROR );
else /* more tries available */
{
abort(); /* scan for ^X */
if( !j ) /* \n first timeout */
{
sprintf( msg, "\n" );
lcl_str( msg );
}
sprintf( msg, "\rtimeout %d ", ++j );
lcl_str( msg );
i = 0;
}
if( !is_cmd )
return( rx() ); /* send back char */
if( (ch = rx()) == CAN )
error( "Received cancellation request" );
return( ch );
}
n( rx() ); /* send back char */
if( (ch = rx()) == CAN )
error